package controller.preprocessor;

import java.awt.event.ActionEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.Semaphore;

import model.cnf.FileCNF;
import model.preprocessor.EPreProcessor;
import model.preprocessor.PreProcessorParameter;
import model.preprocessor.PreProcessorResult;
import model.preprocessor.PreProcessorResultStatus;
import model.preprocessor.RevivalParameter;

/**
 * This class is a implementation of PreProcessor for Revival.
 * 
 * @author GRONOWSKI Stephane svan001@aol.com
 * 
 *         igsat 2010
 * 
 */
public class ReViVal extends PreProcessor {

	public static final String	EXEC					= "revival";
	public static final String	DESCRIPTION				= "Result of the revival execution";

	// public static final int DEFAULT_RETURN_VALUE = 0;
	// public static final int ERR_TIMEOUT = 127;
	/** The default value that is associate to a interrupted execution */
	public static final int		ERR_INTERRUPT			= 130;
	public static final String	TIMEOUT_PREFIX			= "Time out is reached...";
	public static final String	UNSATISFIABLE_PREFIX	= "Proved UNSATISFIABLE";

	private RevivalParameter	param;

	/**
	 * Create a Revival preprocessor.
	 * 
	 * @param directory
	 *            directory where the executable file is.
	 * @throws IOException
	 *             if there is a problem with the file.
	 */
	public ReViVal(File directory) throws IOException {
		if (!directory.isDirectory() || !new File(directory, EXEC).isFile())
			throw new IllegalArgumentException();

		param = new RevivalParameter();
		type = EPreProcessor.REVIVAL;

		try
		{
			path = directory.getCanonicalPath() + File.separator + EXEC;
		} catch (IOException e)
		{
			e.printStackTrace();
			throw new IOException();
		}

	}

	@Override
	public boolean checkParameter() {
		return param.basicCheck();
	}

	@Override
	public PreProcessorParameter getPreProcessorParameter() {
		return param;
	}

	/**
	 * 
	 * @return teh revival parameter.
	 */
	public RevivalParameter getRevivalParameter() {
		return param;
	}

	@Override
	public String[] getCommandLine(String cnfFile) {

		String[] tab = new String[3];

		// bash
		tab[0] = "/bin/sh";
		// -c
		tab[1] = "-c";

		// chemin de revival
		String tmp = correctFormatForLinux(path);
		// stdin
		tmp += " " + correctFormatForLinux(cnfFile);

		if (param.needTimeOut())
			tmp += " " + String.valueOf(param.getTimeOut() / 1000);
		// redirection
		tmp += " > ";
		// destination, fichier de sortie
		tmp += correctFormatForLinux(param.getOut());

		tab[2] = tmp;

		return tab;
	}

	@Override
	public boolean hasTimeout() {
		return true;
	}

	@Override
	public PreProcessorResult runPreProcessor(String[] cmd, String fileOutput, Semaphore manager) {
		PreProcessorResult res = new PreProcessorResult();

		BufferedReader input = null;
		try
		{// lance le processus
			res.setBegin(System.currentTimeMillis());
			setRunning(true);

			try
			{
				process = Runtime.getRuntime().exec(cmd);
			} catch (IOException e)
			{
				throw e;
			}
			finally
			{// libere les sem
				sem.release();
				if (manager != null)
					manager.release();
			}

			String line;
			input = new BufferedReader(new InputStreamReader(process.getErrorStream()));

			while ((line = input.readLine()) != null)
				// System.out.println(line);
				res.addStderr(line + "\n");
		} catch (IOException e)
		{
			// e.printStackTrace();
			res.setStatus(PreProcessorResultStatus.PROBLEM);
			if (process != null)
				process.destroy();
		}
		finally
		{
			try
			{
				if (input != null)
					input.close();
				if (process != null)
					res.setExitVal(process.waitFor());
			} catch (Exception e)
			{
				e.printStackTrace();
				res.setStatus(PreProcessorResultStatus.PROBLEM);
			}
			finally
			{
				try
				{
					sem.acquire();
				} catch (InterruptedException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
					return null;
				}

				setRunning(false);
			}

		}
		// terminer
		res.setEnd(System.currentTimeMillis());

		// System.out.println("val de retour" + res.getExitVal());
		// s'occupe du status
		if (hasBeenStop())
			res.setStatus(PreProcessorResultStatus.STOP);
		else
			if (res.getStatus() != PreProcessorResultStatus.PROBLEM)
				// interrompu
				// System.out.println("stderr =" + res.getStderr());
				if (res.getStderr().startsWith(TIMEOUT_PREFIX))
					res.setStatus(PreProcessorResultStatus.TIMEOUT_REACHED);
				else
					if (res.getStderr().startsWith(UNSATISFIABLE_PREFIX))
						res.setStatus(PreProcessorResultStatus.UNSATISFIABLE);
					else
						res.setStatus(PreProcessorResultStatus.OK);

		// dernier details
		res.setDescription(DESCRIPTION);
		res.setExportPath(fileOutput);
		try
		{
			if (res.getStatus() == PreProcessorResultStatus.OK)
				res.setCnf(new FileCNF(fileOutput));
			// System.out.println("tension \n " + res.getCnf().getStat());
			// res.setStat(new Stat(res.getCnf()));
		} catch (Exception e)
		{
			e.printStackTrace();
			res.setStatus(PreProcessorResultStatus.PROBLEM);
		}

		// libere le sem
		sem.release();

		return res;
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == timer)
			timer.stop();
	}

}
